#public modules
import numpy as np
import matplotlib.pyplot as plt
import tiff_image_manipulation as ti# Pandas read xlsx
import os,sys
import time
import pandas as pd
import copy
from skimage import io, exposure

def disp_initiating_msg(settings_general):
    """Display a initiating messing containing the relevant script settings"""
    print('General Settings:')
    for key,value in settings_general.items():
        print(f'\t{key}: {value}')
    print('\n')

def get_vid_df(df_subset,settings_general,pressures=np.array([-1])):
    """
    Based on the dataframe containing the list of experiments and the experiment ID, the list of 
    videos is imported into another data frame.
    
    The list of pressures 
    """
    #print(df_subset.path)
    dir_exp = df_subset.path.values[0]  

    spreadsheet_file_name_prefix = settings_general["spreadsheet_file_name_prefix"]
    
    name_spreadsheet = fp.find_file_name(dir_exp, sub_string='', prefix=spreadsheet_file_name_prefix, file_ending = 'xlsx')
    #Read the speadsheet containting the list of video details
    if name_spreadsheet == -1:
        raise ValueError(f'The spreadsheet containting the list of video details ({name_spreadsheet}) can not be found.\n Make sure the data hard-drive is connected.\n The directory specified is {dir_exp}.')

    path_exp = dir_exp + '\\' + name_spreadsheet
    if not os.path.exists(path_exp):
        raise ValueError(f'The spreadsheet containting the list of video details ({name_spreadsheet}) can not be found.\n Make sure the data hard-drive is connected.\n The directory specified is {dir_exp}.')
    if settings_general['spreadsheet_file_name_prefix'] == 'exp_list':
        df_exp = pd.read_excel(path_exp, header=1, dtype={'file_nbr':str, 'frame_rate':float, 'p':float, 'n_frames':float, 'duration':float, 'shape':str, 'mask_file_nbr':str})
    elif settings_general['spreadsheet_file_name_prefix'] == '_nd2':
        df_exp = pd.read_excel(path_exp, header=0, dtype={'device_type':str, 'file_nbr':str, 'frame_rate':float, 'p':str, 'n_frames':float, 'duration':float, 'shape':str, 'mask_file_nbr':str})
    else:
        raise ValueError(f'Bad spreadsheet_file_name_prefix value, \'{spreadsheet_file_name_prefix}\'')
    print(f'\tRead spreadsheet: {path_exp}')
    if len(df_exp) == 0:
        raise ValueError('Empty data frame')

    if settings_general['spreadsheet_file_name_prefix'] == 'exp_list':
        #Check the pressure values written in the spreadsheet
        p_unique = df_exp.p.unique()
        p_unique.sort()
        print(f'\tFound {len(p_unique)} unique pressure values in the experiment: {p_unique} mbar')
        if pressures[0] > 1:
            if not all(p_value in p_unique for p_value in pressures):
                raise ValueError(f'Could not find all of the pressure values in the experimental data set ({p_unique} mbar)\n Selected pressure values are: {pressures} mbar')
            
    #Delete rows that lack a file number
    df_exp = df_exp[df_exp['file_nbr'].notna()]  

    df_exp['spreadsheet_file_name'] = name_spreadsheet 
    df_exp['dir_exp'] = dir_exp #Directory of the experiment        

    if 'device_type' in df_exp.columns:
        device_type0 = df_exp['device_type'].values[0]
        if isinstance(device_type0, str):
            if device_type0 == ' ':
                raise ValueError('Could not find the device type. You need to manually fill in the device type in the spreadsheet')
        if isinstance(device_type0, float):
            if np.isnan(device_type0):
                raise ValueError('Could not find the device type. You need to manually fill in the device type in the spreadsheet')

    return df_exp

def create_exp_df(exp_ID, df, settings_general):
    """
    Create a Data Frame based on the parameters of a given experiment. 
    """
    #Make a subset of the experiment list based on the selected experiment ID
    df_subset = df[(df['exp_ID'] == exp_ID)] 
    
    if df_subset.size == 0:
        raise ValueError(f'Could not find the exp ID {exp_ID} in the list of experiments')

    #The list of videos for the experiment is imported into a data frame.
    df_exp = get_vid_df(df_subset, settings_general)

    return df_exp

def get_exp_list_df():  
    """
    Load the experiment list into a Data Frame
    """
    dir_main = r'C:\Users\os4875st\.py\py_DNA_waves\waves analysis'  
    file_name_exp_list = 'list_of_experiments.xlsx'
    path_exp_list = os.path.join(dir_main, 'array_pixilation', file_name_exp_list)
    #print(path_exp_list)
    df = pd.read_excel(path_exp_list, dtype={'exp_ID':str, 'pressures':list, 'device_type':str, 'experiment_type':str, 'pressures_to_ignore':float})
    return df

def delete_to_be_ignored_pressures(df_subset, pressures):
    """
        Deleting pressue values from the list of pressures according to the list of 
        pressure values to be ignored (see list_of_experiments.xlsx)
    """
    pressures_to_ignore = df_subset.pressures_to_ignore.values
    
    for h,p_ignore in enumerate(pressures_to_ignore):        
        inx = np.where(pressures == p_ignore)[0]

        if inx.size != 0:
            inx = inx[0]
            print(f'\t  Ignoring pressue value {p_ignore} from the list of pressures (see list_of_experiments.xlsx)')
            pressures = np.delete(pressures,inx)
    return pressures

def update_pressures(df, exp_ID, pressures):
    """
    Deleting pressue values from the list of pressures according to the list of 
    pressure values to be ignored (see list_of_experiments.xlsx) 
    """
    if pressures[0] > 1:   
        df_subset = df[(df['exp_ID'] == exp_ID)]
        pressures = delete_to_be_ignored_pressures(df_subset, pressures)
    return pressures

def make_subset_of_df_exp(df_exp, settings_general, mag='', p=-1, file_nbr=-1):
    #Make a subset of the experiment list based on the selected experiment ID
    if len(mag) > 0 and p >=0:
        df_vid = df_exp[(df_exp['p'] == p) & (df_exp['mag'] == mag) & (df_exp['order'] == settings_general['file_duplicate_p_order'])] 
        if df_vid.empty:
            df_vid = df_exp[(df_exp['p'] == str(p)) & (df_exp['mag'] == mag) & (df_exp['order'] == settings_general['file_duplicate_p_order'])] 
        if df_vid.empty:
            df_vid = df_exp[(df_exp['p'] == str(p).replace('.',',')) & (df_exp['mag'] == mag) & (df_exp['order'] == settings_general['file_duplicate_p_order'])] 
        if df_vid.empty:
            df_vid = df_exp[(df_exp['p'] == str(int(p))) & (df_exp['mag'] == mag) & (df_exp['order'] == settings_general['file_duplicate_p_order'])]         
        if df_vid.empty:
            file_duplicate_p_order = settings_general['file_duplicate_p_order']
            print(df_exp[df_exp['file_nbr'] == '027'])
            raise ValueError(f'Could not find data with p = {p}, mag = {mag} and order = {file_duplicate_p_order}')
            # print(df_exp.size)
    elif len(file_nbr) > 0:
        df_vid = df_exp[(df_exp['file_nbr'] == file_nbr)] 
        if df_vid.empty:            
            raise ValueError(f'Could not find data with file_nbr = {file_nbr} in the spreadsheet {df_exp.dir_exp.values[0]}')        

    device_type = df_vid.device_type.values[0]
    if isinstance(device_type, (np.ndarray, np.generic) ):
        if np.isnan():
            raise ValueError('Device type entry is empty in the nd2 spreadsheet. Please fill it in.')
    elif isinstance(device_type,(str)):
        if len(device_type) == 0:
            raise ValueError('Device type entry is empty in the nd2 spreadsheet. Please fill it in.')
    return df_vid

def run_func_iterate_exp_list(func, settings_general, file_path, dict_parameters_fun_specific):
    """Run chosen function"""
    if func == 'split_imgs_polarization_mode':
        dict_var_fun_specific = opt.split_imgs_polarization_mode(file_path, settings_general, dict_parameters_fun_specific)
    elif func == 'hsv_image_generation':
        dict_var_fun_specific = opt.hsv_image_generation_v2(file_path, settings_general, dict_parameters_fun_specific)
    elif func == 'array_pixelation':
        dict_var_fun_specific = ap.array_pixelation(file_path, settings_general, dict_parameters_fun_specific)
    else:
        raise ValueError(f'function code {func} is not valid')
    return dict_var_fun_specific

def iterate_exp_list(settings_general, expIDs, dict_parameters_fun_specific, func='', file_nbrs=[], mags=['10x'], pressures=np.array([-1])):
    """Iterates a parameters set in the experiments set the list of experiment IDs.
    Either a list of pressures and list of pressures are set or a list of videograph file numbers.

    Args:
        settings_general (Dictionary): Dictionary containing general settings
        expIDs (list of strings): List of experiment IDs
        dict_parameters_fun_specific (Dictionary): _description_
        func (str, optional): Function to run. Defaults to ''.
        file_nbrs (list, optional): Videograph file numbers. Defaults to [].
        mags (list, optional): List of magnifications to run. Defaults to ['10x'].
        pressures (_type_, optional): List of Pressure difference sto run. Defaults to np.array([-1]).

    Raises:
        ValueError: _description_

    Returns:
        _type_: _description_
    """
    list_v = []
    df = get_exp_list_df()
    disp_initiating_msg(settings_general)
    tic = time.perf_counter() #start time counting
    for i, exp_ID in enumerate(expIDs):
        print(f'==={func}===')
        print(f'Running exp ID {i+1}/{len(expIDs)}: {exp_ID}')
        df_exp = create_exp_df(exp_ID, df, settings_general)
        dict_parameters_fun_specific['df_exp'] = df_exp #add to dictionary for specific functions        
        dict_parameters_fun_specific['dict_list_of_exp'] = df[(df['exp_ID'] == exp_ID)].to_dict('records')[0]
        update_pressures(df, exp_ID, pressures)
        print(f'\tIn dir {df_exp.dir_exp.values[0]}')
        if pressures[0] > 0 and len(file_nbrs) == 0:
            #Run the script for each pressure value
            for j,p in enumerate(pressures):
                print('============================================================================================')
                print('============================================================================================')
                print('============================================================================================')
                print(f'\n  Running pressure value {j+1}/{len(pressures)}: {p} mbar')
                
                #Run the script for each magnification selected: 
                for k,mag in enumerate(mags):
                    print(f'  Running magnification {k+1}/{len(mags)}: {mag}')

                    #Make a subset of the experiment list based on the selected experiment ID
                    df_vid = make_subset_of_df_exp(df_exp, settings_general, mag=mag, p=p)
                    dict_parameters_fun_specific['df_vid'] = df_vid #add to dictionary for specific functions
                    dict_parameters_fun_specific['d_vid'] = df_vid.to_dict('records')[0] #add to dictionary for specific functions

                    #Update the experiment data frame
                    df_exp.loc[df_vid.index[0]] = df_vid.loc[df_vid.index[0]]  #is this really needed?

                    file_path = df_vid.file_path.values[0]

                    # ==========================================================
                    # === Run specific function ================================
                    dict_var_fun_specific = run_func_iterate_exp_list(func, settings_general, file_path, dict_parameters_fun_specific)
                    # ==========================================================
                    # ==========================================================
                    if 'v' in dict_var_fun_specific:
                        list_v.append(dict_var_fun_specific['v'])
                toc = time.perf_counter()
                print(f'\tPressure val {j+1}/{len(pressures)}: {p} mbar complete after {toc - tic:0.2f} seconds or {(toc - tic)/60:0.2f} min')   
        elif pressures[0] == -1 and len(file_nbrs) > 0:
            for j,f in enumerate(file_nbrs):
                print(f'\n  Running file number {j+1}/{len(file_nbrs)}: #{f}')

                df_vid = make_subset_of_df_exp(df_exp, settings_general, file_nbr=f)
                dict_parameters_fun_specific['d_vid'] = df_vid.to_dict('records')[0] #add to dictionary for specific functions. The orientation 'records' creates a list for each row. In our case, we only have one row so the first one is chosen.
                dict_parameters_fun_specific['df_vid'] = df_vid
                file_path = df_vid.file_path.values[0]

                # ==========================================================
                # === Run specific function ================================
                dict_var_fun_specific = run_func_iterate_exp_list(func, settings_general, file_path, dict_parameters_fun_specific)
                # ==========================================================
                # ==========================================================
                if 'v' in dict_var_fun_specific:
                    list_v.append(dict_var_fun_specific['v'])
                toc = time.perf_counter()
                print(f'\tfile number {j+1}/{len(file_nbrs)}: #{f} complete in {toc - tic:0.2f} seconds or {(toc - tic)/60:0.2f} min')   
        else:
            raise ValueError('Either run by pressures and mags or run based on the file numbers...')

    toc = time.perf_counter()
    print(f'Completed script in {toc - tic:0.2f} seconds or {(toc - tic)/60:0.2f} minutes.')

    if 'v' in dict_var_fun_specific:
        return list_v
    else:
        return ''